import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'package:path/path.dart' as path;
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/foundation.dart'; // 用于调试输出

// 定义C结构体 (占位，当前示例通过进程方式调用 exe)
final class FunASRModelConfig extends Struct {
  external Pointer<Utf8> model_dir;
  external Pointer<Utf8> quantize;
  external Pointer<Utf8> bladedisc;
  external Pointer<Utf8> vad_dir;
  external Pointer<Utf8> vad_quant;
  external Pointer<Utf8> punc_dir;
  external Pointer<Utf8> punc_quant;
  external Pointer<Utf8> itn_dir;
  external Pointer<Utf8> lm_dir;
}

class AsrFFI {
  static final AsrFFI _instance = AsrFFI._internal();
  factory AsrFFI() => _instance;
  AsrFFI._internal();

  late String _workRoot; // 临时工作根目录
  late String _dllDir; // 提取后的 dlls 目录
  late String _modelDir; // 提取后的模型目录
  bool _initialized = false;
  final List<String> _recognitionHistory = [];

  // 提取指定资源文件到目标目录
  Future<void> _extractAsset(String assetPath, String destPath) async {
    try {
      final bytes = await rootBundle.load(assetPath);
      final out = File(destPath);
      await out.create(recursive: true);
      await out.writeAsBytes(bytes.buffer.asUint8List());
      debugPrint('[AsrFFI] extracted: $assetPath -> $destPath');
    } catch (e) {
      debugPrint('[AsrFFI] extract failed: $assetPath -> $e');
    }
  }

  // 提取 DLL 文件列表到目标目录
  Future<int> _extractDllAssets(String destDir) async {
    final dllFiles = [
      'funasr-onnx-offline.exe',
      'funasr-onnx-online-asr.exe',
      'funasr-onnx-2pass.exe',
      'funasr-onnx-2pass-rtf.exe',
      'onnxruntime.dll',
      'avcodec-60.dll',
      'avdevice-60.dll',
      'avfilter-9.dll',
      'avformat-60.dll',
      'avutil-58.dll',
    ];

    int copied = 0;
    for (final fileName in dllFiles) {
      try {
        await _extractAsset(
          'assets/dlls/$fileName',
          path.join(destDir, fileName),
        );
        copied++;
      } catch (e) {
        debugPrint('[AsrFFI] skip dll: $fileName -> $e');
      }
    }
    return copied;
  }

  // 检查并获取模型文件的实际路径
  String? _findModelFile(String modelDir, List<String> candidates) {
    debugPrint('[AsrFFI] Searching for model in: $modelDir');
    for (final candidate in candidates) {
      final filePath = path.join(modelDir, candidate);
      debugPrint('[AsrFFI] Checking model file: $filePath');
      if (File(filePath).existsSync()) {
        final fileSize = File(filePath).lengthSync();
        debugPrint('[AsrFFI] Found model file: $filePath ($fileSize bytes)');
        return filePath;
      } else {
        debugPrint('[AsrFFI] Model file not found: $filePath');
      }
    }
    return null;
  }

  Future<void> _validateOnnxModel(String modelPath) async {
    try {
      debugPrint('[AsrFFI] Validating ONNX model: $modelPath');
      final file = File(modelPath);
      final bytes = await file.readAsBytes();

      // 检查文件头是否为ONNX格式
      if (bytes.length > 8) {
        final header = String.fromCharCodes(bytes.take(8));
        debugPrint('[AsrFFI] Model file header: ${header.codeUnits}');

        // 简单检查是否包含ONNX标识
        final content = String.fromCharCodes(bytes.take(1024));
        if (content.contains('onnx') || content.contains('ONNX')) {
          debugPrint('[AsrFFI] Model appears to be valid ONNX format');
        } else {
          debugPrint('[AsrFFI] Warning: Model may not be valid ONNX format');
        }
      }
    } catch (e) {
      debugPrint('[AsrFFI] Error validating model: $e');
    }
  }

  // 从 assets 提取目录到临时目录，供 exe 运行时查找依赖
  Future<void> init() async {
    if (_initialized) return;
    final temp = await getTemporaryDirectory();
    _workRoot = path.join(temp.path, 'funasr_runtime');
    _dllDir = path.join(_workRoot, 'dlls');

    // 优先尝试使用量化模型目录（可能兼容性更好）
    final quantModelDir = path.join(
      Directory.current.path,
      'assets',
      'models',
      'speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-onnx',
    );
    if (Directory(quantModelDir).existsSync()) {
      final quantFile = File(path.join(quantModelDir, 'model_quant.onnx'));
      if (quantFile.existsSync()) {
        debugPrint('[AsrFFI] Using quantized model directory: $quantModelDir');
        _modelDir = quantModelDir;
      } else {
        _modelDir = path.join(
          Directory.current.path,
          'assets',
          'models',
          'paraformer-zh-streaming-onnx',
        );
        debugPrint('[AsrFFI] Using streaming model directory: $_modelDir');
      }
    } else {
      // 如果量化模型不可用，使用流式模型
      _modelDir = path.join(
        Directory.current.path,
        'assets',
        'models',
        'paraformer-zh-streaming-onnx',
      );
      debugPrint('[AsrFFI] Using streaming model directory: $_modelDir');
    }

    await Directory(_dllDir).create(recursive: true);

    // 调试：打印工作目录、模型与 dll 临时目录
    debugPrint('[AsrFFI] workRoot=$_workRoot');
    debugPrint('[AsrFFI] dllTempDir=$_dllDir');
    debugPrint('[AsrFFI] modelDir=$_modelDir');

    // 从 assets 中提取 dlls 到临时目录
    final dllCopied = await _extractDllAssets(_dllDir);
    debugPrint('[AsrFFI] dlls extracted: $dllCopied files to $_dllDir');

    _initialized = true;
  }

  // 获取 dll/可执行文件目录（仅使用项目内路径）
  List<String> _candidateDllDirs() {
    final candidates = <String>[];

    // 1) 项目工作目录下的 assets/dlls（开发阶段）
    final projectDlls = path.join(Directory.current.path, 'assets', 'dlls');
    if (Directory(projectDlls).existsSync()) {
      candidates.add(projectDlls);
    }

    // 2) 临时解压目录（提取的 dlls）
    if (Directory(_dllDir).existsSync()) {
      candidates.add(_dllDir);
    }

    // 调试：打印 DLL 搜索候选目录
    debugPrint('[AsrFFI] DLL search candidates:\n${candidates.join('\n')}');

    return candidates;
  }

  // 在候选目录中解析可执行文件完整路径
  String _resolveExePath(String exeName) {
    final tried = <String>[];
    for (final dir in _candidateDllDirs()) {
      final exePath = path.join(dir, exeName);
      tried.add(exePath);
      if (File(exePath).existsSync()) {
        // 调试：找到的可执行文件路径
        debugPrint('[AsrFFI] Resolved $exeName: $exePath');
        return exePath;
      }
    }
    // 调试：找不到时打印尝试过的路径
    debugPrint(
      '[AsrFFI] $exeName not found. Tried paths:\n${tried.join('\n')}',
    );
    throw Exception('未找到 $exeName，已尝试路径:\n${tried.join('\n')}');
  }

  // 进程方式调用 funasr-onnx-offline.exe
  Future<String> recognizeOffline({
    required String wavFilePath,
    bool useQuant = true,
    int sampleRate = 16000,
    bool tryFallback = true, // 新增：是否尝试备选方案
  }) async {
    if (!_initialized) {
      await init();
    }

    debugPrint('[AsrFFI] Starting offline recognition...');
    debugPrint('[AsrFFI] Input file: $wavFilePath');
    debugPrint(
      '[AsrFFI] useQuant: $useQuant, sampleRate: $sampleRate, tryFallback: $tryFallback',
    );

    // 尝试主要识别
    try {
      return await _performOfflineRecognition(
        wavFilePath,
        useQuant,
        sampleRate,
      );
    } catch (e) {
      debugPrint('[AsrFFI] Primary offline recognition failed: $e');

      if (tryFallback && e.toString().contains('model IR version')) {
        debugPrint(
          '[AsrFFI] Detected ONNX IR version incompatibility, trying fallbacks...',
        );

        // 备选方案1：尝试相反的量化设置
        if (useQuant) {
          debugPrint('[AsrFFI] Fallback 1: Trying non-quantized model...');
          try {
            return await _performOfflineRecognition(
              wavFilePath,
              false,
              sampleRate,
            );
          } catch (e2) {
            debugPrint('[AsrFFI] Fallback 1 failed: $e2');
          }
        } else {
          debugPrint('[AsrFFI] Fallback 1: Trying quantized model...');
          try {
            return await _performOfflineRecognition(
              wavFilePath,
              true,
              sampleRate,
            );
          } catch (e2) {
            debugPrint('[AsrFFI] Fallback 1 failed: $e2');
          }
        }

        // 备选方案2：尝试使用不同的模型目录
        debugPrint(
          '[AsrFFI] Fallback 2: Trying alternative model directory...',
        );
        try {
          return await _performOfflineRecognitionWithAlternativeModel(
            wavFilePath,
            sampleRate,
          );
        } catch (e3) {
          debugPrint('[AsrFFI] Fallback 2 failed: $e3');
        }
      }

      rethrow;
    }
  }

  Future<String> _performOfflineRecognition(
    String wavFilePath,
    bool useQuant,
    int sampleRate,
  ) async {
    final exePath = _resolveExePath('funasr-onnx-offline.exe');
    final exeDir = path.dirname(exePath);

    debugPrint('[AsrFFI] Exe path: $exePath');
    debugPrint('[AsrFFI] Working directory: $exeDir');
    debugPrint('[AsrFFI] DLL directory: $_dllDir');
    debugPrint('[AsrFFI] Model directory: $_modelDir');

    // 检查模型目录
    if (!Directory(_modelDir).existsSync()) {
      throw Exception('Model directory not found: $_modelDir');
    }

    // 检查并找到可用的模型文件
    var finalQuant = useQuant;
    var modelPath = _findModelFile(_modelDir, [
      'model_quant.onnx',
      'model.onnx',
    ]);

    if (useQuant && modelPath != null && !modelPath.contains('quant')) {
      debugPrint('[AsrFFI] model_quant.onnx not found, using model.onnx');
      finalQuant = false;
    } else if (modelPath == null) {
      throw Exception('No model file found in $_modelDir');
    }

    final env = Map<String, String>.from(Platform.environment);
    final sep = Platform.isWindows ? ';' : ':';
    env['PATH'] = exeDir + sep + _dllDir + sep + (env['PATH'] ?? '');

    final args = <String>[
      '--model-dir',
      _modelDir,
      '--quantize',
      finalQuant ? 'true' : 'false',
      '--bladedisc',
      'false',
      '--wav-path',
      wavFilePath,
      '--audio-fs',
      sampleRate.toString(),
    ];

    debugPrint('[AsrFFI] Command: $exePath ${args.join(' ')}');
    debugPrint('[AsrFFI] Working dir: $exeDir');
    debugPrint('[AsrFFI] Model path: $modelPath');

    // 验证模型文件
    await _validateOnnxModel(modelPath);

    final processResult = await Process.run(
      exePath,
      args,
      environment: env,
      workingDirectory: exeDir,
    );

    debugPrint('[AsrFFI] Process exit code: ${processResult.exitCode}');
    debugPrint('[AsrFFI] Stdout: ${processResult.stdout}');

    if (processResult.stderr.isNotEmpty) {
      debugPrint('[AsrFFI] Stderr: ${processResult.stderr}');

      // 检查是否是ONNX版本兼容性错误
      if (processResult.stderr.toString().contains('model IR version')) {
        debugPrint('[AsrFFI] ONNX IR version compatibility issue detected');
        debugPrint(
          '[AsrFFI] Current error indicates model IR version is too high',
        );
        debugPrint(
          '[AsrFFI] Solution 1: Try using model_quant.onnx instead of model.onnx',
        );
        debugPrint('[AsrFFI] Solution 2: Use a model with lower IR version');
        debugPrint(
          '[AsrFFI] Solution 3: Update ONNX Runtime to support higher IR versions',
        );
      }

      // 检查其他常见错误
      if (processResult.stderr.toString().contains(
            'No such file or directory',
          ) ||
          processResult.stderr.toString().contains('cannot find')) {
        debugPrint('[AsrFFI] File not found error - check model and DLL paths');
      }
    }

    if (processResult.exitCode != 0) {
      throw Exception(
        'ASR process failed with exit code ${processResult.exitCode}: ${processResult.stderr}',
      );
    }

    // 解析输出
    final stdout = processResult.stdout.toString();
    final stderr = processResult.stderr.toString();
    final allOutput = '$stdout\n$stderr';

    // funasr 输出中包含形如：INFO wav_default_id : 文本
    String text = '';
    final lines = allOutput.split('\n');
    for (final line in lines) {
      final idx = line.indexOf(' : ');
      if (idx > 0) {
        final candidate = line.substring(idx + 3).trim();
        if (candidate.isNotEmpty) {
          text = candidate; // 取最后一次有效输出
        }
      }
    }

    if (text.isEmpty) {
      text = stdout.trim().isEmpty ? '未解析到结果' : stdout.trim();
    }

    // 更新识别历史
    _recognitionHistory.add(text);
    debugPrint('[AsrFFI] Recognition result: $text');
    return text;
  }

  Future<String> _performOfflineRecognitionWithAlternativeModel(
    String wavFilePath,
    int sampleRate,
  ) async {
    debugPrint('[AsrFFI] Trying alternative model directory...');

    // 尝试使用另一个模型目录
    final alternativeModels = [
      'speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-onnx',
      'paraformer-zh-streaming-onnx',
    ];

    final originalModelDir = _modelDir;

    for (final altModel in alternativeModels) {
      if (originalModelDir.contains(altModel)) continue; // 跳过当前正在使用的模型

      final altModelPath = path.join(path.dirname(originalModelDir), altModel);
      debugPrint('[AsrFFI] Trying alternative model: $altModelPath');

      if (Directory(altModelPath).existsSync()) {
        _modelDir = altModelPath;
        try {
          return await _performOfflineRecognition(
            wavFilePath,
            true,
            sampleRate,
          );
        } catch (e) {
          debugPrint('[AsrFFI] Alternative model $altModel failed: $e');
        } finally {
          _modelDir = originalModelDir; // 恢复原始路径
        }
      } else {
        debugPrint(
          '[AsrFFI] Alternative model directory not found: $altModelPath',
        );
      }
    }

    throw Exception('All alternative models failed');
  }

  // 使用在线可执行文件对单个 wav 做"文件流式模拟"推理
  Future<String> recognizeOnlineFromFile({
    required String wavFilePath,
    bool useQuant = true,
    int sampleRate = 16000,
  }) async {
    if (!_initialized) {
      await init();
    }

    final exePath = _resolveExePath('funasr-onnx-online-asr.exe');
    final exeDir = path.dirname(exePath);

    debugPrint('[AsrFFI] Executing: $exePath');
    debugPrint('[AsrFFI] Working dir: $exeDir');
    debugPrint('[AsrFFI] Model dir: $_modelDir');

    final env = Map<String, String>.from(Platform.environment);
    final sep = Platform.isWindows ? ';' : ':';
    env['PATH'] = exeDir + sep + _dllDir + sep + (env['PATH'] ?? '');

    // 若量化模型不存在则自动回退
    var finalQuant = useQuant;
    if (finalQuant &&
        !File(path.join(_modelDir, 'model_quant.onnx')).existsSync()) {
      debugPrint('[AsrFFI] model_quant.onnx not found, fallback to non-quant');
      finalQuant = false;
    }

    final args = <String>[
      '--model-dir',
      _modelDir,
      '--quantize',
      finalQuant ? 'true' : 'false',
      '--bladedisc',
      'false',
      '--wav-path',
      wavFilePath,
      '--audio-fs',
      sampleRate.toString(),
    ];

    final processResult = await Process.run(
      exePath,
      args,
      environment: env,
      workingDirectory: exeDir,
    );

    if (processResult.exitCode != 0) {
      throw Exception(
        'Online ASR process failed with exit code ${processResult.exitCode}: ${processResult.stderr}',
      );
    }

    final stdout = processResult.stdout.toString();
    if (stdout.trim().isEmpty) {
      // 尝试读取结果文件
      final resultTxt = File(path.join(exeDir, 'result.txt'));
      if (resultTxt.existsSync()) {
        final fileContent = await resultTxt.readAsString();
        if (fileContent.isNotEmpty) {
          debugPrint('[AsrFFI] Got result from result.txt: $fileContent');
          return fileContent.trim();
        }
      }
      throw Exception('No output from online ASR process');
    }

    debugPrint('[AsrFFI] Raw online output: $stdout');

    // 解析在线识别的输出
    final lines = stdout.trim().split('\n');
    final results = <String>[];

    for (final line in lines) {
      if (line.contains('"text"')) {
        // 假设JSON格式输出，提取text字段
        final jsonMatch = RegExp(r'"text"\s*:\s*"([^"]*)"').firstMatch(line);
        if (jsonMatch != null) {
          results.add(jsonMatch.group(1)!);
        }
      }
    }

    if (results.isEmpty) {
      return stdout.trim();
    }

    final result = results.join(' ').trim();
    debugPrint('[AsrFFI] Online recognition result: $result');
    return result;
  }
}
